package com.atguigu.gmall.mq.config;

import com.atguigu.gmall.mq.config.component.impl.RabbitServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.amqp.RabbitTemplateConfigurer;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.retry.support.RetryTemplate;

/**
 * @Author:lyd
 * @Date:2023/9/5 19:40
 * @Version:1.0
 * @Description 给容器中放交换机、队列组件，项目启动，如果mq服务器没有这些队列交换机，就会自动创建
 **/
@EnableRabbit//开启基于注解的Rabbit
@Slf4j
@AutoConfigureAfter(RedisAutoConfiguration.class)//在redis自动配置加载完后，再加载这个配置
@Import(RabbitServiceImpl.class)
@Configuration
public class AppRabbitAutoConfiguration {


    /**
     * 由于springboot自带的RabbitTemplate没有提供confirm、return回调处理，需要我们自己手动指定RabbitTemplate
     * 依此来处理两个回调的内容
     *
     * @param configurer
     * @param connectionFactory
     * @return
     */
    @Bean
    public RabbitTemplate rabbitTemplate(RabbitTemplateConfigurer configurer,
                                         ConnectionFactory connectionFactory) {
        RabbitTemplate template = new RabbitTemplate();
        configurer.configure(template, connectionFactory);

        /**
         * @Nullable CorrelationData correlationData, 发送消息的相关数据
         * boolean ack,确认状态，表示服务器的交换机是否收到
         * @Nullable String cause 如果有问题，会打印错误原因
         */
        template.setConfirmCallback((CorrelationData correlationData,
                                     boolean ack,
                                     String cause) -> {
            //confirm回调在任何时候都触发
            log.info("confirm-callback: 消息：{}，接受状态：{}，错误原因：{}" + correlationData, ack, cause);
        });


        /**
         * message： 刚才的消息
         * replyCode： 回复码；
         * replyText： 回复文本；
         * exchange：交换机
         * routingKey：路由键
         */
        template.setReturnCallback((Message message,
                                    int replyCode,
                                    String replyText,
                                    String exchange,
                                    String routingKey) -> {
            //return回调只有在错误的时候才会触发,成功消息不会触发
            log.info("return-callback: 消息；{}，回复码：{}，回复文本：{}，交换机：{}，路由键：{}"
                    + message, replyCode, replyText, exchange, routingKey);
            //【把消息保存到数据库定期重试】。 重试多次依然失败，人工介入。
        });

        //设置重试模板
        template.setRetryTemplate(new RetryTemplate());

        return template;
    }

}
